
#ifndef ITEMS_HPP
#define ITEMS_HPP

#include "common.hpp"
#include "transfer.hpp"
#include "gauges.hpp"

#include <QtWi>
#include <QtWiTmpl>

#include <fstream>
#include <vector>
#include <iostream>
#include <string>

#include <typeinfo>


////////////////////
// Status console //
////////////////////

class Console : public ITemplate
{
private:
	ITextBox *tb;
public:

	enum Symbols { note, warning, error, endl };
	
	Console(IWidget &parent, Ifactory &f, const int &x, const int &y,
		const int &w, const int &h) : ITemplate(parent, f)
	{
		tb = ftory().makeTextBox(&parent, "", x, y, ITextBox::Multi);
		tb->resize(w, h);
		tb->show();
	}

	Console& operator<<(const int &i)
	{
		tb->appendText(const_cast<char*>(to_string<int>(i).c_str()));
		return *this;
	}

	Console& operator<<(const char *s)
	{
		tb->appendText(s);
		return *this;
	}

	Console& operator<<(std::string s)
	{
		tb->appendText(const_cast<char*>(s.c_str()));
		return *this;
	}

	Console& operator<<(Console::Symbols s)
	{
		switch(s) {
			case endl:
				tb->appendText("\n");
				break;
			case note:
				tb->appendText("* ");
				break;
			case warning:
				tb->appendText("* WARNING: ");
				break;
			case error:
				tb->appendText("* ERROR: ");
				break;
		}
		return *this;
	}

};

////////////////
// Usage bars //
////////////////

class UsageBar : public ITimerHandler
{
protected:
	IProgressBar *bar;
	ILabel *lbl;
	int current;
	ValueLogger log;

private:
	virtual int utilization() const = 0;

public:
	static const int label_width;
	static const int usg_bar_height;
	
	UsageBar(Ifactory &f, IWidget &p, const char *caption, const int &x, const int &y,
		 const int &len, const char *logname) : ITimerHandler(bar), log(logname)
	{
		const int all_h = general_widget_height;
		lbl = f.makeLabel(&p, caption, x, y);
		lbl->resize(label_width, all_h);
		bar = f.makeProgressBar(&p, "", x + label_width + 5, y, 0, 100, 1);
		bar->resize(len - label_width, all_h);
		bar->setCurrent(0);
	}

	void timeout()
	{
		int usg;

		if((usg = utilization()) < 0) 
			return;

		current = usg;
		bar->setCurrent(usg);
		bar->refresh();
		if(connected) log.out() << current << std::endl;
	}

	IProgressBar* getBar() {
		return bar;
	}

};


class CPUusage : public UsageBar
{
	
public:
	static const int update_interval;
	static const char *cpu_proc;

private:
	int utilization() const;
	
public:
	
	CPUusage(Ifactory& f, IWidget &p, const char *caption, const int &x, const int &y,
		 const int &len, const char *logname) :
		UsageBar(f, p, caption, x, y, len, logname) { }
};


class MEMusage : public UsageBar
{
public:
	static const int update_interval;
	static const char *mem_proc;

	const enum Type
	{
		Memory,
		Swap
	} type;
	
private:
	int utilization() const;

public:
	MEMusage(Ifactory& f, IWidget &p, const char *caption,
		 MEMusage::Type t, const int &x, const int &y, const int &len, const char *logname) :
		UsageBar(f, p, caption, x, y, len, logname), type(t) { }
};

class UsageBars : public IWidgetWrapper<IRawWidget>
{
private:
	CPUusage cpu;
	MEMusage mem;
	MEMusage swap;
public:
	UsageBars(IWidget &parent, Ifactory &fact, const int &x, const int &y,
		  const int &w, const int &h) :
		IWidgetWrapper<IRawWidget>(parent, fact, x, y, w, h),
		cpu(CPUusage(ftory(), *widget(), "CPU:", 0, UsageBar::usg_bar_height, w, "cpu")),
		mem(MEMusage(ftory(), *widget(), "Mem:", MEMusage::Memory, 0,
			     2 * UsageBar::usg_bar_height, w, "mem")),
		swap(MEMusage(ftory(), *widget(), "Swap:", MEMusage::Swap, 0,
			      3 * UsageBar::usg_bar_height, w, "swap"))
	{
		ftory().getApp().registerTimer(&cpu, cpu.getBar(), CPUusage::update_interval);
		ftory().getApp().registerTimer(&mem, mem.getBar(), MEMusage::update_interval);
		ftory().getApp().registerTimer(&swap, swap.getBar(), MEMusage::update_interval);
	}
};


///////////////
// Connector //
///////////////


// Panels

class ConnectorPanel : public IWidgetWrapper<IRadioGroup>, protected IActionHandler
{
	typedef std::vector<IRadioButton*> btn_vec;
	
private:
	btn_vec *buttons;
	ITextBox *tb;
	int current_y;

public:
	// Mock rtti; neccessary for GuiConnector
	enum {
		Network = 0,
		File,
	};

	const int id;
	
public:

	// todo: destructors, remove vector
	// todo: removeButton and deconstruct/delete all buttons in vector
	
	static const int connector_panel_width;
	static const int content_offsetx;
	
	ConnectorPanel(IWidget &parent, Ifactory &fact, int height, int cid) :
		IWidgetWrapper<IRadioGroup>(parent, fact, 0, 0, connector_panel_width, height),
		buttons(new btn_vec), current_y(0), id(cid)
	{
		tb = ftory().makeTextBox(widget(), "", content_offsetx, current_y, ITextBox::Single);
		tb->resize(connector_panel_width - 2 * content_offsetx, general_textbox_height);
	}
	
	int selected() const
	{
		int idx = 0;
	
		for(btn_vec::const_iterator i = buttons->begin(); i != buttons->end(); i++, idx++) {
			if((*i)->checked())
				return idx;
		}

		return -1;
	}

	ITextBox* textbox() const
	{
		return tb;
	}

	std::string boxText() const
	{
		return tb->text();
	}


	void call_handler()
	{
		handler(*tb);
	}
	
protected:
	void addRadioButton(const char *caption, bool checked)
	{
		current_y += general_widget_height + general_widget_buffer / 2;
		
		IRadioButton *b = ftory().makeRadioButton(widget(), caption,
							  content_offsetx, current_y);
		
		b->setChecked(checked);
		buttons->push_back(b);
		
		tb->move(content_offsetx, current_y + general_widget_height +
			 general_widget_buffer * 2);

		ftory().getApp().registerAction(*b, IAction::CLICK, this);
	}

};

class NetworkPanel : public ConnectorPanel
{

public:

	// Radio button indices
	enum {
		TCP = 0,
		UART,
		CAN
	};
	
	NetworkPanel(IWidget &parent, Ifactory &fact, int height) :
		ConnectorPanel(parent, fact, height, ConnectorPanel::Network)
	{
		
		addRadioButton("TCP", true);
		addRadioButton("Serial", false);
		addRadioButton("CAN", false);
	}

public:

	// Default texts
	void handler(IWidget &w)
	{
		std::string dtxt;
		
		switch(selected()) {

			case TCP:
				sndr_recv(
#ifdef SNDIP
					  SNDIP
#else					  
					  "127.0.0.1:6666"
#endif
					  ,
#ifdef RECIP
					  RECIP
#else
					  "127.0.0.1:6666"
#endif
					  );
				break;

			case UART:
				sndr_recv("/dev/ttyS0", "/dev/ttyS0");
				break;

			case CAN:
				sndr_recv("can0", "can1");
				break;		
		}

		textbox()->setText(const_cast<char*>(dtxt.c_str()));
		
	}
	
};

class FilePanel : public ConnectorPanel
{
public:

	enum {
		FILE = 0,
		PIPE,
		RANDOM
	};
	
	FilePanel(IWidget &parent, Ifactory &fact, int height) :
		ConnectorPanel(parent, fact, height, ConnectorPanel::File)
	{
		addRadioButton("File", false);
		addRadioButton("Pipe", true);
		addRadioButton("Random", false);

	}

	// Default texts
	void handler(IWidget &w)
	{
		std::string dtxt;
		
		switch(selected()) {

			case FILE:
				sndr_recv("/dev/zero", "/tmp/ufile");
				break;

			case PIPE:
				sndr_recv("/tmp/ubench/fifo", "/tmp/ubench/fifo");
				break;

			case RANDOM:
				sndr_recv("/dev/random", "/dev/null");
				break;
		}

		textbox()->setText(const_cast<char*>(dtxt.c_str()));
	}
};

class SettingsPanel : public IWidgetWrapper<IRadioGroup>
{

private:
	IRadioButton *send;
	IRadioButton *recv;
	IButton *establ;
	
public:

	static const char *establ_caption;
	static const char *cancel_caption;
	
	void establish_button_setup(bool connectable)
	{
		if(connectable)
			establ->setCaption(establ_caption);
		else
			establ->setCaption(cancel_caption);
	}

	std::string caption() const
	{
		return establ->caption(); 
	}

	// todo: action handler
	SettingsPanel(IWidget &parent, Ifactory &fact, IActionHandler *gui_connector, int height) :
		IWidgetWrapper<IRadioGroup>(parent, fact, 0, 0,
					    ConnectorPanel::connector_panel_width, height)
	{
		widget()->setCaption("Settings");

		send = ftory().makeRadioButton(widget(), "Sender", ConnectorPanel::content_offsetx,
					       general_widget_height);
		
		recv = ftory().makeRadioButton(widget(), "Receiver", ConnectorPanel::content_offsetx,
					       general_widget_height * 2 + general_widget_buffer);

		establ = ftory().makeButton(widget(), "", ConnectorPanel::content_offsetx,
					     general_widget_height * 4 + general_widget_buffer);
		
		recv->setChecked(true);
		establ->resize(ConnectorPanel::connector_panel_width -
			       2 * ConnectorPanel::content_offsetx, general_button_height);

		recv->show();
		send->show();
		establish_button_setup(true);
		establ->show();

		ftory().getApp().registerAction(*send, IAction::CLICK, gui_connector);
		ftory().getApp().registerAction(*recv, IAction::CLICK, gui_connector);
		ftory().getApp().registerAction(*establ, IAction::CLICK, gui_connector);
	}
};

class GuiConnector : public IWidgetWrapper<IRawWidget>, public IActionHandler, public ITimerHandler
{
private:

	// Connector
	
	ConnectorPanel *source_panel;
	ConnectorPanel *destination_panel;

	NetworkPanel net_panel;
	FilePanel file_panel;
	SettingsPanel set_panel;

	DataSource *source;
	DataDestination *sink;

	Connector *connector;
	IMutexLock<Connector*> connector_lock;

	// Chunk size slider
	ILabeledSlider lbls;
	IMutexLock<ILabeledSlider> lbls_lock;

	// Max delay slider
	ILabeledSlider slider_delay;
	IMutexLock<ILabeledSlider> slider_delay_lock;

	// Bit rate label
	ILabeledTextBox lblb;

	// Total transferred label
	ILabeledTextBox lbltot;

	// Gauges
	Gauge *source_gauge;
	Gauge *sink_gauge;

	ICheckButton *sink_gauge_chk;
	ICheckButton *source_gauge_chk;
	ICheckButton *write_logs_chk;
	Timing timing;

	// Log files
	ValueLogger rate_logger;
	ValueLogger total_logger;
	ValueLogger time_logger;
	ValueLogger chunk_logger;
	ValueLogger delay_logger;
	ValueLogger loadavg_logger;
	ValueLogger s11n_logger;
	
private:
	
	void establish();
	void connect();
	void disconnect();

	vecstr tokenize_ip_port_str();
	
public:
	static const int gui_connector_overall_height;
	static const int destination_panel_offsetx;
	static const int gui_connector_height;
	static const int connect_panels_offsety;
	
	static const int gauge_control_offsety;
	static const int gauge_control_height;

	static const int rate_label_offsety;
	
	static const int update_interval;
	static const int status_query_interval;
	
	GuiConnector(IWidget &parent, Ifactory &f, int x, int y, int w, int h);

	void refresh();

	virtual void handler(IWidget &snd);
	
	virtual void timeout();

};


#endif
